package backgammon;

import java.util.ArrayList;

/**
 *
 * @author Jacob Kwitkoski
 * @version 1.0
 */
public class GameLogic {

    public LocalPlayer localPlayer;

    private RemotePlayer remotePlayer;

    private StatPanel stats;

    private Board board;

    private boolean localTurn;

    private ArrayList<Move> validMoves;

    private BoardDisplayer displayer;

    public GameLogic() {
        localPlayer  = new LocalPlayer(Board.localPieceColor);
        remotePlayer = new RemotePlayer(Board.remotePieceColor);
        board        = new Board(this);
        stats        = new StatPanel(this);
        localTurn    = true;
        validMoves   = new ArrayList();

        displayer = new GUIBoardDisplayer("Backgammon",
                                          board,
                                          stats);

        updateLocalPips();
        updateRemotePips();
    }

    public ArrayList<Move> getValidMoves() {
        return validMoves;
    }

    public final void updateLocalPips() {
        stats.setLocalPips(localPlayer.getPips());
    }

    public final void updateRemotePips() {
        stats.setRemotePips(remotePlayer.getPips());
    }

    public final boolean canMoveToPosition(final Position position) {
        boolean canMoveTo = false;
        int index = position.getArrayIndex();
        if (validMoves != null) {
            for (Move move : validMoves) {
                if (move.getToPosition() == index) {
                    canMoveTo = true;
                }
            }
        }
        return canMoveTo;
    }

    public final void processMovesForRoll() {
        if (localTurn) {
            processMovesForLocalPlayer();
        } else {
            processMovesForRemotePlayer();
        }
    }

    private final void processMovesForLocalPlayer() {
        Position[] positions = board.getPositions();

        if (localPlayer.hasPiecesOnBar()) {
            //only valid moves will be from bar position
            findMovesForPosition(positions[Board.BAR_POS_L]);
            System.out.println("PIECES ON BAR");
        } else {
            //get the moves for each position
            for (Position position : positions) {
                if (position.hasPieces()) {
                    if (position.getPieceColor() == Board.localPieceColor) {
                        findMovesForPosition(position);
                    }
                }
            }
        }
        board.setPositions(positions);

        boolean canMove = false;
        for (Position position : positions) {
            if (position.hasMoves()) {
                canMove = true;
                break;
            }
        }

        if (!canMove) {
            System.out.println("PLAYER HAS NO MOVES!");
            endTurn();
        }

        System.out.println("\nMOVES PROCESSED FOR LOCAL PLAYER");
    }

    private final void processMovesForRemotePlayer() {
        Position[] positions = board.getPositions();

        if (remotePlayer.hasPiecesOnBar()) {
            //only valid moves will be from bar position
            findMovesForPosition(positions[Board.BAR_POS_R]);
        } else {
            //get the moves for each position
            for (Position position : positions) {
                if (position.hasPieces()) {
                    if (position.getPieceColor() == Board.remotePieceColor) {
                        findMovesForPosition(position);
                    }
                }
            }
        }
        board.setPositions(positions);

        boolean canMove = false;
        for (Position position : positions) {
            if (position.hasMoves()) {
                canMove = true;
                break;
            }
        }

        if (!canMove) {
            System.out.println("PLAYER HAS NO MOVES!");
            endTurn();
        }
        System.out.println("\nMOVES PROCESSED FOR REMOTE PLAYER");
    }

    private final void findMovesForPosition(Position position) {
        validMoves = null;
        validMoves = new ArrayList();

        int currentIndex = position.getArrayIndex();

        validMoves.add(new Move(currentIndex, Move.ORIGINAL_POSITION));
        if (localTurn) {
            checkLocalMoves(currentIndex);
        } else {
            checkRemoteMoves(currentIndex);
        }
        position.setMoves(validMoves);

        System.out.println("MOVES ADDED FOR POSITION: " + position.getArrayIndex());
    }

    public final boolean positionHasMoves(final Position position) {
        boolean hasMoves = false;
        if (position.hasMoves()) {
            hasMoves = true;
        }
        return hasMoves;
    }
//    public final boolean positionHasMoves(final Position position) {
//        validMoves = null;
//        validMoves = new ArrayList();
//
//        boolean hasMoves = true;
//        int currentIndex = position.getArrayIndex();
//
//        if (position.hasPieces()) {
//            if (localTurn) {
//                if (position.getPieceColor() == Board.localPieceColor) {
//                    validMoves.add(new Move(currentIndex,
//                                    Move.ORIGINAL_POSITION));
//                    //System.out.println("\nAdded default move." + validMoves.get(0));
//                    checkLocalMoves(currentIndex);
//                }
//            } else {
//                if (position.getPieceColor() == Board.remotePieceColor) {
//                    validMoves.add(new Move(currentIndex,
//                                    Move.ORIGINAL_POSITION));
//                    //System.out.println("\nAdded default move." + validMoves.get(0));
//                    checkRemoteMoves(currentIndex);
//                }
//            }
//        }
//
//        if (validMoves.size() < 2) {
//            hasMoves = false;
//        }
//
//        return hasMoves;
//    }

    private void checkLocalMoves(final int index) {
        Position[] positions = board.getPositions();
        Dice currentRoll = stats.getDice();

        if (!currentRoll.isDie1used()) {
            int pos1 = index - currentRoll.getDie1();
            System.out.println("die1 isn't used");
            System.out.println("Index: " + pos1);
            if (pos1 > 1) {
                Position die1Pos = positions[pos1];
                checkMovesForLocalPosition(index, die1Pos, 1);
            } else {
                if (localPlayer.canBearOff()) {
                    //new bear off move
                    System.out.println("\nBearing off!");
                } else {
                    System.out.println("\nCan't bear off yet!");
                }
            }
        }

        if (!currentRoll.isDie2used()) {
            int pos2 = index - currentRoll.getDie2();
            System.out.println("die2 isn't used");
            if (pos2 > 1) {
                Position die2Pos = positions[pos2];
                checkMovesForLocalPosition(index, die2Pos, 2);
            } else {
                if (localPlayer.canBearOff()) {
                    //new bear off move
                    System.out.println("\nBearing off!");
                } else {
                    System.out.println("\nCan't bear off yet!");
                }
            }
        }

        if (!currentRoll.isDie1used() && !currentRoll.isDie2used()) {
            int pos3 = index - currentRoll.getDie1() - currentRoll.getDie2();
        }
        //System.out.println("\nValid moves: " + validMoves);
    }

    private void checkRemoteMoves(final int index) {
        Position[] positions = board.getPositions();
        Dice currentRoll = stats.getDice();

        if (!currentRoll.isDie1used()) {
            int pos1 = index + currentRoll.getDie1();
            if (pos1 < 26) {
                Position die1Pos = positions[pos1];
                checkMovesForRemotePosition(index, die1Pos, 1);
            } else {
                if(remotePlayer.canBearOff()) {
                    //new bear off move
                    System.out.println("\nBearing off!");
                } else {
                    System.out.println("\nCan't bear off yet!");
                }
            }
        }

        if (!currentRoll.isDie2used()) {
            int pos2 = index + currentRoll.getDie2();
            if (pos2 < 26) {
                Position die2Pos = positions[pos2];
                checkMovesForRemotePosition(index, die2Pos, 2);
            } else {
                if (remotePlayer.canBearOff()) {
                    //new bear off move
                    System.out.println("\nBearing off!");
                } else {
                    System.out.println("\nCan't bear off yet!");
                }
            }
        }

        if (!currentRoll.isDie1used() && !currentRoll.isDie2used()) {
            int pos3 = index + currentRoll.getDie1() + currentRoll.getDie2();
        }
        //System.out.println("\nValid moves: " + validMoves);
    }

    private boolean checkMovesForLocalPosition(int            fromPosition,
                                               final Position toPosition,
                                               final int      dieNum) {
        boolean hasMoves = false;

        if (toPosition.hasPieces()) {
            if (toPosition.getPieceColor() == Board.localPieceColor) {
                validMoves.add(new Move(fromPosition,
                                        toPosition.getArrayIndex(),
                                        Move.ADD_MOVE,
                                        dieNum));
                hasMoves = true;
                //System.out.println("\nValid moves: " + validMoves);
            } else {
                //System.out.println("Dice roll gets us to position"
                //        + " with opposite color");
                if (toPosition.isBlottable()) {
                    System.out.println("Blot move");
                    validMoves.add(new Move(fromPosition,
                                            toPosition.getArrayIndex(),
                                            Move.BLOT_MOVE,
                                            dieNum));
                    hasMoves = true;
                    //System.out.println("\nValid moves: " + validMoves);
                } else {
                    //System.out.println("Can't move to position: "
                    //        + toPosition.getArrayIndex() + ", position blocked.");
                }
            }
        } else {
            validMoves.add(new Move(fromPosition,
                                    toPosition.getArrayIndex(),
                                    Move.ADD_MOVE,
                                    dieNum));
            hasMoves = true;
        }

        return hasMoves;
    }

    private boolean checkMovesForRemotePosition(int            fromPosition,
                                                final Position toPosition,
                                                int            dieNum) {
        boolean hasMoves = false;
        if (toPosition.hasPieces()) {
            if (toPosition.getPieceColor() == Board.remotePieceColor) {
                validMoves.add(new Move(fromPosition,
                                        toPosition.getArrayIndex(),
                                        Move.ADD_MOVE,
                                        dieNum));
                hasMoves = true;
                //System.out.println("\nValid moves: " + validMoves);
            } else {
                //System.out.println("Dice roll gets us to position"
                //        + " with opposite color");
                if (toPosition.isBlottable()) {
                    validMoves.add(new Move(fromPosition,
                                            toPosition.getArrayIndex(),
                                            Move.BLOT_MOVE,
                                            dieNum));
                    hasMoves = true;
                    //System.out.println("\nValid moves: " + validMoves);
                } else {
                    //System.out.println("Can't move to position: "
                    //        + toPosition.getArrayIndex() + ", position blocked.");
                }
            }
        } else {
            validMoves.add(new Move(fromPosition,
                                    toPosition.getArrayIndex(),
                                    Move.ADD_MOVE,
                                    dieNum));
            hasMoves = true;
        }

        return hasMoves;
    }

    public final void completeMove(final Move     move,
                                   final Position position) {
        Piece piece = board.getSelectedPiece();
        int pipChange = move.getFromPosition() - move.getToPosition();

        if (move.getMoveType().equals(Move.BLOT_MOVE)) {
            Piece blottedPiece;
            blottedPiece = position.removePiece();
            if (blottedPiece.getColor() == Board.localPieceColor) {
                
                localPlayer.setPiecesOnBar(true);

                localPlayer.changePips(Board.BAR_POS_L - blottedPiece.getArrayIndex());
                blottedPiece.setArrayIndex(Board.BAR_POS_L);
                board.setPieceOnPosition(Board.BAR_POS_L, blottedPiece);
                stats.setLocalPips(localPlayer.getPips());
                stats.repaint();
            } else {
                
                remotePlayer.setPiecesOnBar(true);

                remotePlayer.changePips(blottedPiece.getArrayIndex() - Board.BAR_POS_R);
                blottedPiece.setArrayIndex(Board.BAR_POS_R);
                board.setPieceOnPosition(Board.BAR_POS_R, blottedPiece);
                stats.setRemotePips(remotePlayer.getPips());
                stats.repaint();
            }
        } else {
            if (piece.getColor() == Board.localPieceColor) {
                localPlayer.changePips(-1 * pipChange);
                stats.setLocalPips(localPlayer.getPips());
                stats.repaint();
            } else {
                remotePlayer.changePips(pipChange);
                stats.setRemotePips(remotePlayer.getPips());
                stats.repaint();
            }
        }
        piece.setArrayIndex(position.getArrayIndex());
        position.addPiece(piece);
        piece = null;
        board.setSelectedPiece(piece);

        refreshDice(move);
    }

    private void refreshDice(final Move move) {
        if (move.usesDie1()) {
            stats.getDice().useDie1();
        }

        if (move.usesDie2()) {
            stats.getDice().useDie2();
        }

        if (stats.getDice().isDie1used() && stats.getDice().isDie2used()) {
            endTurn();
        }
        stats.repaint();
    }

    private void endTurn() {
        if (localTurn) {
            localTurn = false;
            stats.setLocalTurn(localTurn);
            stats.enableRoll();
        } else {
            localTurn = true;
            stats.setLocalTurn(localTurn);
            stats.enableRoll();
        }
        stats.repaint();
    }
}
